# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\PyInstaller\lib\modulegraph\zipio.py
"""
A helper module that can work with paths
that can refer to data inside a zipfile

XXX: Need to determine if isdir("zipfile.zip")
should return True or False. Currently returns
True, but that might do the wrong thing with
data-files that are zipfiles.
"""
import os as _os, zipfile as _zipfile, errno as _errno, time as _time, sys as _sys, stat as _stat

_DFLT_DIR_MODE = (
    _stat.S_IFDIR
    | _stat.S_IXOTH
    | _stat.S_IXGRP
    | _stat.S_IXUSR
    | _stat.S_IROTH
    | _stat.S_IRGRP
    | _stat.S_IRUSR
)
_DFLT_FILE_MODE = _stat.S_IFREG | _stat.S_IROTH | _stat.S_IRGRP | _stat.S_IRUSR
if _sys.version_info[0] == 2:
    from StringIO import StringIO as _BaseStringIO
    from StringIO import StringIO as _BaseBytesIO

    class _StringIO(_BaseStringIO):
        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_value, traceback):
            self.close()
            return False

    class _BytesIO(_BaseBytesIO):
        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_value, traceback):
            self.close()
            return False


else:
    from io import StringIO as _StringIO
    from io import BytesIO as _BytesIO


def _locate(path):
    full_path = path
    if _os.path.exists(path):
        return (path, None)
    else:
        rest = []
        root = _os.path.splitdrive(path)
        while path:
            if path != root:
                path, bn = _os.path.split(path)
                rest.append(bn)
                if _os.path.exists(path):
                    break

        if path == root:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")
        if not _os.path.isfile(path):
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")
        rest.reverse()
        return (path, ("/").join(rest).strip("/"))


_open = open


def open(path, mode="r"):
    if "w" in mode or "a" in mode:
        raise IOError(_errno.EINVAL, path, "Write access not supported")
    else:
        if "r+" in mode:
            raise IOError(_errno.EINVAL, path, "Write access not supported")
    full_path = path
    path, rest = _locate(path)
    if not rest:
        return _open(path, mode)
        try:
            zf = _zipfile.ZipFile(path, "r")
        except _zipfile.error:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        try:
            data = zf.read(rest)
        except (_zipfile.error, KeyError):
            zf.close()
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        zf.close()
        if mode == "rb":
            return _BytesIO(data)
        if _sys.version_info[0] == 3:
            data = data.decode("ascii")
        return _StringIO(data)


def listdir(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        pass
    if not _os.path.isfile(path):
        return _os.listdir(path)
        try:
            zf = _zipfile.ZipFile(path, "r")
        except _zipfile.error:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        result = set()
        seen = False
        try:
            for nm in zf.namelist():
                if rest is None:
                    seen = True
                    value = nm.split("/")[0]
                    if value:
                        result.add(value)
                elif nm.startswith(rest):
                    if nm == rest:
                        seen = True
                        value = ""
                    else:
                        if nm[len(rest)] == "/":
                            seen = True
                            value = nm[len(rest) + 1 :].split("/")[0]
                        else:
                            value = None
                        if value:
                            result.add(value)

        except _zipfile.error:
            zf.close()
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        zf.close()
        if not seen:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")
        return list(result)


def isfile(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        ok = _os.path.isfile(path)
        if ok:
            try:
                zf = _zipfile.ZipFile(path, "r")
                return False
            except (_zipfile.error, IOError):
                return True

            return False
        zf = None
    try:
        zf = _zipfile.ZipFile(path, "r")
        info = zf.getinfo(rest)
        zf.close()
        return True
    except (KeyError, _zipfile.error):
        if zf is not None:
            zf.close()
        try:
            info = zf.getinfo(rest + "/")
        except KeyError:
            pass
        else:
            return False

        rest = rest + "/"
        for nm in zf.namelist():
            if nm.startswith(rest):
                return False

        raise IOError(_errno.ENOENT, full_path, "No such file or directory")


def isdir(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        ok = _os.path.isdir(path)
        if not ok:
            try:
                zf = _zipfile.ZipFile(path, "r")
            except (_zipfile.error, IOError):
                return False

            return True
        return True
    zf = None
    try:
        try:
            zf = _zipfile.ZipFile(path)
        except _zipfile.error:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        try:
            info = zf.getinfo(rest)
        except KeyError:
            pass
        else:
            return False

        rest = rest + "/"
        try:
            info = zf.getinfo(rest)
        except KeyError:
            pass
        else:
            return True

        for nm in zf.namelist():
            if nm.startswith(rest):
                return True

        raise IOError(_errno.ENOENT, full_path, "No such file or directory")
    finally:
        if zf is not None:
            zf.close()


def islink(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        return _os.path.islink(path)
        try:
            zf = _zipfile.ZipFile(path)
        except _zipfile.error:
            raise IOError(_errno.ENOENT, full_path, "No such file or directory")

        try:
            try:
                info = zf.getinfo(rest)
            except KeyError:
                pass
            else:
                return False

            rest += "/"
            try:
                info = zf.getinfo(rest)
            except KeyError:
                pass
            else:
                return False

            for nm in zf.namelist():
                if nm.startswith(rest):
                    return False

            raise IOError(_errno.ENOENT, full_path, "No such file or directory")
        finally:
            zf.close()


def readlink(path):
    full_path = path
    path, rest = _locate(path)
    if rest:
        raise OSError(_errno.ENOENT, full_path, "No such file or directory")
    return _os.readlink(path)


def getmode(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        return _os.stat(path).st_mode
    zf = None
    try:
        zf = _zipfile.ZipFile(path)
        info = None
        try:
            info = zf.getinfo(rest)
        except KeyError:
            pass

        if info is None:
            try:
                info = zf.getinfo(rest + "/")
            except KeyError:
                pass

            if info is None:
                rest = rest + "/"
                for nm in zf.namelist():
                    if nm.startswith(rest):
                        break
                else:
                    raise IOError(_errno.ENOENT, full_path, "No such file or directory")

                return _DFLT_DIR_MODE
            if info.external_attr >> 16 != 0:
                return _stat.S_IFREG | info.external_attr >> 16
            return _DFLT_FILE_MODE
    except KeyError:
        if zf is not None:
            zf.close()
        raise IOError(_errno.ENOENT, full_path, "No such file or directory")


def getmtime(path):
    full_path = path
    path, rest = _locate(path)
    if not rest:
        return _os.path.getmtime(path)
    zf = None
    try:
        zf = _zipfile.ZipFile(path)
        info = None
        try:
            info = zf.getinfo(rest)
        except KeyError:
            pass

        if info is None:
            try:
                info = zf.getinfo(rest + "/")
            except KeyError:
                pass

            if info is None:
                rest = rest + "/"
                for nm in zf.namelist():
                    if nm.startswith(rest):
                        break
                else:
                    raise IOError(_errno.ENOENT, full_path, "No such file or directory")

                return _os.path.getmtime(path)
        return _time.mktime(info.date_time + (0, 0, -1))
    except KeyError:
        if zf is not None:
            zf.close()
        raise IOError(_errno.ENOENT, full_path, "No such file or directory")
